class Vue {
    constructor(options) {
        this.$options = options
        this._data = options.data
        this.initData()
        this.initWatch()
    }

    initData() {
        const data = this._data
        const keys = Object.keys(data)
        // 将data里所有key代理到vue实例上
        for(let key of keys) {
            Object.defineProperty(this, key, {
                enumerable: true,
                configurable: true,
                get: function proxyGetter() {
                    return data[key]
                },
                set: function proxySetter(val) {
                    data[key] = val
                }
            })
        }

        observe(data)
    }

    initWatch() {
        const watch = this.$options.watch
        const keys = Object.keys(watch)
        for (const key of keys) {
            this.$watch(key, watch[key])
        }
    }

    $watch(exp, cb) {
        new Watcher(this, exp, cb)
    }
}

function isObject(obj) {
    return obj !== null && typeof obj === 'object'
}
// 判断当前观察data类型，如果是对象创建Observe对象进行reactive
function observe(data) {
    if(!isObject(data)) return
    new Observer(data)
}
class Observer {
    constructor(data) {
        this.walk(data)
    }
    walk(data) {
        const keys = Object.keys(data)
        for(let key of keys) {
            let value = data[key]
            defineReactive(data, key, value)
        }
    }
}

function defineReactive(obj, key, value) {
    observe(obj[key])
    let dep = new Dep()
    Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get: function reactiveGetter(){
            // console.log(`data的${key}取值`)
            if(Dep.target) {
                dep.depend()
            }
            return value
        },
        set: function reactiveSetter(val) {
            if(val === value) {
                return
            }
            // console.log(`设置${key}为${val}`)
            dep.notify()
            value = val
        }
    })
}

class Watcher {
    constructor(vm, exp, cb) {
        this.vm = vm
        this.exp = exp
        this.cb = cb
        this.get()
    }
    get() {
        Dep.target = this
        this.vm[this.exp]
        Dep.target = null          
    }
    run() {
        this.cb.call(vm)
    }
}

class Dep {
    constructor() {
        this.subs = []
    }
    depend(){
        if(Dep.target) {
            this.subs.push(Dep.target)
        }
    }
    notify(){
        this.subs.forEach(watcher => {
            watcher.run()
        })
    }
}

// let vm = new Vue({
//     data:{
//       message:'hello vue.js',
//       name:'zs'
//     },
//     watch:{
//       message() {
//         this.name = 'ls'
//         console.log('message发生了变化1');
//       },
//       name() {
//         console.log('123');
//       }
//     }
//   })
//   vm.message = 2